//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.Diagnostics.CodeAnalysis;
using System.Resources;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Microsoft.Practices.EnterpriseLibrary.Data;

namespace EntLibContrib.Resource.Managers
{
	/// <summary>
	/// Custom Resource Reader to read resources from a database
	/// </summary>
	[SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface")]
	[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
	public class DataResourceReader : IResourceReader
	{
		#region Fields
		private Database database;
		private string baseName;
		private string cultureName;
		private bool useDataNodes;
		private bool disposed;
		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets a value indicating whether to use data node values from the reader.
		/// </summary>
		/// <value><c>true</c> if the reader is to use data nodes; otherwise, <c>false</c>.</value>
		public bool UseDataNodes
		{
			get { return useDataNodes; }
			set { useDataNodes = value; }
		}
		#endregion

		#region Construction
		/// <summary>
		/// Initializes a new instance of the <see cref="T:DataResourceReader"/> class.
		/// </summary>
		/// <param name="database">The database.</param>
		/// <param name="baseName">the base name for the set of resources.</param>
		/// <param name="cultureInfo">The culture information.</param>
		public DataResourceReader (Database database, string baseName, CultureInfo cultureInfo)
		{
			if (database == null)
				throw new ArgumentNullException("database");
				
			this.database = database;

			if (baseName == null)
				this.baseName = String.Empty;
			else
				this.baseName = baseName;

			if (cultureInfo == null)
				this.cultureName = "neutral";
			else
				this.cultureName = (cultureInfo.Name.Length == 0) ? "neutral" : cultureInfo.Name;
		}

		/// <summary>
		/// Use C# destructor syntax for finalization code.
		/// </summary>
		/// <remarks>
		/// This destructor will run only if the Dispose method
		/// does not get called.
		/// It gives your base class the opportunity to finalize.
		/// Do not provide destructors in types derived from this class.
		/// </remarks>
		~DataResourceReader()
		{
			Dispose(false);
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Returns an <see cref="T:System.Collections.IDictionaryEnumerator"></see> of the resources for this reader.
		/// </summary>
		/// <returns>
		/// A dictionary enumerator for the resources for this reader.
		/// </returns>
		public IDictionaryEnumerator GetEnumerator ()
		{
			Hashtable resources = new Hashtable();

			DbCommand loadItemsCommand = database.GetStoredProcCommand("GetResources");
			database.AddInParameter(loadItemsCommand, "@BaseName", DbType.String, baseName);
			database.AddInParameter(loadItemsCommand, "@Culture", DbType.String, cultureName);
			using(IDataReader resourceItems = database.ExecuteReader(loadItemsCommand))
			{
				while (resourceItems.Read())
				{
					string name = resourceItems["Name"].ToString();
					object value = DeserializeValue(resourceItems["Value"]);
					if (useDataNodes)
					{
						ResourceDataNode resourceDataNode;
						string typeName = resourceItems["Type"].ToString();
						if (typeName == typeof(ResourceFileRef).AssemblyQualifiedName)
							resourceDataNode = new ResourceDataNode(name, (ResourceFileRef)value);
						else
							resourceDataNode = new ResourceDataNode(name, value);
						resourceDataNode.Comment = resourceItems["Comment"].ToString();
						resources.Add(name, resourceDataNode);
					}
					else
						resources.Add(name, value);
				}
			}
			return resources.GetEnumerator();
		}

		/// <summary>
		/// Closes the resource reader after releasing any resources associated with it.
		/// </summary>
		public void Close ()
		{
		}

		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
		/// </returns>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}

		/// <summary>
		/// Releases all resources used by the <see cref="T:System.ComponentModel.Component"></see>.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Deserializes the value.
		/// </summary>
		/// <param name="value">The serialized value.</param>
		/// <returns>the deserialized value</returns>
		private static object DeserializeValue (object value)
		{
			if ( value == DBNull.Value )
				value = null;
			else
			{
				byte[] valueBytes = (byte[])value;
				value = SerializationUtility.ToObject(valueBytes);
			}
			return value;
		}

		/// <summary>
		/// Releases the unmanaged resources used by the <see cref="T:System.ComponentModel.Component"></see> and optionally releases the managed resources.
		/// </summary>
		/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
		protected virtual void Dispose(bool disposing)
		{
			if (!disposed)
			{
				// Dispose of resources held by this instance.
				// (This process is not shown here.)

				// Set the sentinel.
				disposed = true;

				// Suppress finalization of this disposed instance.
				if (disposing)
				{
					GC.SuppressFinalize(this);
				}
			}
		}
		#endregion
	}
}
